/*
    This file is part of Darwin.

    Darwin is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Foobar is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Foobar; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    
    Darwin uses GALib
    Copyright (c) 1995-1996 Massachusetts Institute of Technology (MIT)
    Copyright (c) 1996-2005 Matthew Wall
    All rights reserved.
    
    Darwin uses ConfigFile
    Copyright (c) 2004 Richard J. Wagner
     
    Darwin, a Genetic Algorithm to solve Timetabling problem
    Copyright (c) 2006 Christian S. Perone
*/  

#include "timegenome.h"

void TimeGenome::AddDisciplina(std::vector<int> &v, int codprofessordisciplina, int turno, int creditos)
{
     std::vector<int> slots_free;

     switch(creditos)
     {
          case 4:
               switch(turno)
               {
                    case turnos::NOITE:
                         for(int i=turnos::NOITE_BEGIN; i<=turnos::NOITE_END; i+=2)
                         {
                              if(v[i+1]) continue;
                              if(!v[i]) slots_free.push_back(i);
                         }
                         break;

                    case turnos::TARDE_NOITE:
                         for(int i=turnos::TARDE_BEGIN; i<=turnos::NOITE_END; i+=2)
                         {
                              if(v[i+1]) continue;
                              if(!v[i]) slots_free.push_back(i);
                         }
                         break;

                    case turnos::MANHA_NOITE:
                         for(int i=turnos::MANHA_BEGIN; i<=turnos::MANHA_END; i+=2)
                         {
                              if(v[i+1]) continue; 
                              if(!v[i]) slots_free.push_back(i);
                         }
                         for(int i=turnos::NOITE_BEGIN; i<=turnos::NOITE_END; i+=2)
                         {
                              if(v[i+1]) continue;
                              if(!v[i]) slots_free.push_back(i);
                         }
                         break;
               }
               break;
               
          case 2:
               switch(turno)
               {
                    case turnos::NOITE:
                         for(int i=turnos::NOITE_BEGIN; i<=turnos::NOITE_END; i++)
                         {
                              if(i>turnos::NOITE_BEGIN)
                                   if(v[i-1]) continue;
                              if(!v[i]) slots_free.push_back(i);
                         }
                         break;

                    case turnos::TARDE_NOITE:
                         for(int i=turnos::TARDE_BEGIN; i<=turnos::NOITE_END; i++)
                         {
                              if(i>turnos::TARDE_BEGIN)
                                   if(v[i-1]) continue;
                              if(!v[i]) slots_free.push_back(i);
                         }
                         break;

                    case turnos::MANHA_NOITE:
                         for(int i=turnos::MANHA_BEGIN; i<=turnos::MANHA_END; i++)
                         {
                              if(i>turnos::MANHA_BEGIN)
                                   if(v[i-1]) continue;
                              if(!v[i]) slots_free.push_back(i);
                         }
                         for(int i=turnos::NOITE_BEGIN; i<=turnos::NOITE_END; i++)
                         {
                              if(i>turnos::NOITE_BEGIN)
                                   if(v[i-1]) continue;
                              if(!v[i]) slots_free.push_back(i);
                         }
                         break;
               }
               break;
     }
     
     if(!slots_free.size())
     {
//          cout << endl << "Warning: Cannot add disciplina to slot, no more slots." << endl;
//          std::cin.get();
          return;
     }
     
     int random_pos = GARandomInt(0, slots_free.size()-1);
     v[slots_free[random_pos]] = codprofessordisciplina;
}

TimeGenome::TimeGenome(int niveis)
: GAGenome(Init, Mutate, Compare)
{
     evaluator(Evaluate);
     crossover(CrossOnePointNivel);
     // Essa CrossOnePointTurno quebra os building-blocks do cromossomo
     //crossover(CrossOnePointTurno);
     assert(niveis>0);
     mNiveis = niveis;
     mListNiveis = new std::vector<int> * [niveis];
     
     for(int i=0; i<niveis; i++)
          mListNiveis[i] = new std::vector<int>(turnos::TAMANHO, 0);
}

TimeGenome::TimeGenome(const TimeGenome &orig)
{
     mNiveis = 0;
     copy(orig);
}

TimeGenome::~TimeGenome()
{
     for(int i=0; i<mNiveis; i++)
          delete mListNiveis[i];
     delete [] mListNiveis;
}

void TimeGenome::copy(const GAGenome &orig)
{
     if (&orig==this) return;
     GAGenome::copy(orig);
     TimeGenome &genome = (TimeGenome &) orig;

     if(mNiveis == genome.mNiveis)
     {
          for(int i=0; i<mNiveis; i++)
          {
               std::vector<int> &vNiveis = *mListNiveis[i];
               vNiveis = *genome.mListNiveis[i];
          }
     }
     else
     {
          if(mNiveis>0)
          {
               for(int i=0; i<mNiveis; i++)   
                    delete mListNiveis[i];
               delete [] mListNiveis;
          }
          
          mNiveis = genome.mNiveis;
          mListNiveis = new std::vector<int> * [mNiveis];
          
          for(int i=0; i<mNiveis; i++)
                 mListNiveis[i] = new std::vector<int>(*genome.mListNiveis[i]);
     }
}

void TimeGenome::Init(GAGenome &gen)
{
     TimeGenome &genome = (TimeGenome &) gen;
     
     for(int i=0; i<genome.mNiveis; i++)
          for(uint j=0; j<genome.mListNiveis[i]->size(); j++)
               genome.GetNivelList(i)[j]=0;

     MapOferecimento::t_vector &vect_oferecimento = map_oferecimento.GetVector();
     MapOferecimento::t_iterator it_oferecimento;
     assert(vect_oferecimento.size()>0);
     
     MapProfessorDisciplina::t_vector &vect_profdis = map_professordisciplina.GetVector();
     MapProfessorDisciplina::t_iterator it_profdis;
     assert(vect_profdis.size()>0);
     
     MapDisciplina::t_iterator it_disciplina;
     
     for(int i=0; i<genome.GetNiveis(); i++)
     {
          for(it_oferecimento=vect_oferecimento.begin();
              it_oferecimento!=vect_oferecimento.end();
              it_oferecimento++)
          {
               if(it_oferecimento->nivel!=(i+1)) continue;
               std::vector<int> pool_dis;
               
               for(it_profdis=vect_profdis.begin();
                   it_profdis!=vect_profdis.end();
                   it_profdis++)
               {
                    if(it_profdis->coddisciplina!=it_oferecimento->coddisciplina) continue;
                    pool_dis.push_back(it_profdis->codprofessordisciplina);
               }
               
               assert(pool_dis.size()>0);
               int random_dis = pool_dis[GARandomInt(0, pool_dis.size()-1)];

               it_profdis = map_professordisciplina.GetInfo(random_dis);
               
               it_disciplina = map_disciplina.GetInfo(it_profdis->coddisciplina);
               assert(it_disciplina != map_disciplina.GetEnd());

               AddDisciplina(*genome.mListNiveis[i],
                             random_dis,
                             it_oferecimento->turno,
                             it_disciplina->creditos);
          }
     }


     genome._evaluated = gaFalse;
}

// colocar isso em um laço soh ? 
int TimeGenome::Mutate(GAGenome &gen, float p)
{
     int nmut = 0;
     std::vector<int> pool_dis;
     TimeGenome &genome = (TimeGenome &) gen;
     if(p<=0.0) return nmut;
     
     MapProfessorDisciplina::t_iterator it_profdis;
     MapOferecimento::t_iterator it_oferecimento;
     MapDisciplina::t_iterator it_disciplina;

     // primera etapa da mutação
     // mutar o codprofessordisciplina para outro, desde que tenha
     // mais codprofessordisciplina para a mesma disciplina
     
     // Niveis
     for(int i=0; i<genome.mNiveis; i++)
     {
          // Cada CodProfessorDisciplina dos vetores
          for(int j=0; j<turnos::TAMANHO; j++)
          {
               if(genome.GetNivelList(i)[j]==0) continue;
               // Flip Coin!
               if(GAFlipCoin(p))
               {
                    it_profdis = map_professordisciplina.GetInfo(genome.GetNivelList(i)[j]);
                    assert(it_profdis!=map_professordisciplina.GetEnd());
                    pool_dis.clear();
                    
                    int coddisciplina = it_profdis->coddisciplina;
                    int codprofessordisciplina = it_profdis->codprofessordisciplina;
                    
                    for(it_profdis=map_professordisciplina.GetBegin();
                        it_profdis!=map_professordisciplina.GetEnd();
                        it_profdis++)
                    {
                         if(
                              (it_profdis->coddisciplina!=coddisciplina) ||
                              (it_profdis->codprofessordisciplina==codprofessordisciplina)
                         ) continue;
                         
                         pool_dis.push_back(it_profdis->codprofessordisciplina);
                    }
                    
                    if(pool_dis.size()>0)
                    {
                         genome.GetNivelList(i)[j] = pool_dis[GARandomInt(0, pool_dis.size()-1)];
                         nmut++;                    
                    }
               } // flip coin
          } // cada elemento do vetor
     } // niveis
     
     
     // segunda etapa da mutação, swaps
     
     // Niveis
     for(int i=0; i<genome.mNiveis; i++)
     {
          // Cada CodProfessorDisciplina dos vetores
          for(int j=0; j<turnos::TAMANHO; j++)
          {
               if(genome.GetNivelList(i)[j]==0) continue;
               // Flip Coin!
               if(GAFlipCoin(p))
               {
                    it_profdis = map_professordisciplina.GetInfo(genome.GetNivelList(i)[j]);
                    assert(it_profdis!=map_professordisciplina.GetEnd());
                    
                    it_oferecimento = map_oferecimento.GetInfo(it_profdis->coddisciplina, i+1);
                    assert(it_oferecimento!=map_oferecimento.GetEnd());
                    
                    it_disciplina = map_disciplina.GetInfo(it_profdis->coddisciplina);
                    assert(it_disciplina!=map_disciplina.GetEnd());
                    
                    genome.GetNivelList(i)[j]=0;
                    
                    AddDisciplina(*genome.mListNiveis[i],
                                  it_profdis->codprofessordisciplina,
                                  it_oferecimento->turno,
                                  it_disciplina->creditos);
                    nmut++;
               }     
          }
     }

     if(nmut) genome._evaluated = gaFalse;
     return nmut;
}

float TimeGenome::Compare(const GAGenome &gen1, const GAGenome &gen2)
{
     float diversity=0.0;

     TimeGenome &genome1 = (TimeGenome &) gen1;
     TimeGenome &genome2 = (TimeGenome &) gen2;
     
     // impossivel calcular diversidade entre genomas com
     // quantidade de niveis diferentes
     if(genome1.mNiveis!=genome2.mNiveis) return -1;
     
     for(int i=0; i<genome1.mNiveis; i++)
          for(uint j=0; j<genome1.GetNivelList(i).size(); j++)
               diversity += (genome1.GetNivelList(i)[j] == genome2.GetNivelList(i)[j]) ? 0 : 1;
     
     return diversity/(genome1.mNiveis*turnos::TAMANHO);
}

float TimeGenome::Evaluate(GAGenome &gen)
{
     const float weight_found_oferecimento 	=  3.5;
     const float weight_stillturn		= 0.8;
     const float weight_preferencial	= 0.2;
     const float weight_clash			= 1.5;
     const float weight_laboratorio 	= 0.1;
     const float weight_horas			= 0.1;
     const float weight_disponibilidade = 2.0;
     const float weight_feriado         = 0.5;
     const float weight = 0.5;

     float score=0.0;
     TimeGenome &genome = (TimeGenome &) gen;
     
     MapOferecimento::t_iterator          it_oferecimento;
     MapProfessorDisciplina::t_iterator   it_profdis;
     MapDisciplina::t_iterator            it_disciplina;
     MapCurso::t_iterator		       it_curso;
     MapProfessor::t_iterator             it_professor;
     MapDisponibilidade::t_iterator       it_disponibilidade;
     
     std::vector<int> laboratorio_slot(turnos::TAMANHO, 0);
     
     std::map<int,int> professor_horas;
     
     
     // Constraints

     // #1 Todas disciplinas do oferecimento estão presentes e no turno certo?
     //    e este professordisciplina que está no slot é preferencial ?

     // #2 Aula no mesmo horário com o mesmo professor

     // Constraint #1
     for(int nivel=0; nivel<genome.mNiveis; nivel++)
     {
          for(it_oferecimento=map_oferecimento.GetBegin();
              it_oferecimento!=map_oferecimento.GetEnd();
              it_oferecimento++)
          {
               if(it_oferecimento->nivel!=nivel+1) continue;
               
               switch(it_oferecimento->turno)
               {
                    case turnos::NOITE:
                         for(int i=turnos::NOITE_BEGIN; i<=turnos::NOITE_END; i++)
                         {
                              if(!genome.GetNivelList(nivel)[i]) continue;
                              it_profdis = map_professordisciplina.GetInfo(genome.GetNivelList(nivel)[i]);
                              assert(it_profdis!=map_professordisciplina.GetEnd());
                              if(it_profdis->coddisciplina==it_oferecimento->coddisciplina)
                              {
                                   score+=(weight*weight_found_oferecimento);
                                   if(it_profdis->preferencial) score+=(weight*weight_preferencial);
                              }
                              
                         }
                         break;               
               
                    case turnos::TARDE_NOITE:
                         for(int i=turnos::TARDE_BEGIN; i<=turnos::NOITE_END; i++)
                         {
                              if(!genome.GetNivelList(nivel)[i]) continue;
                              it_profdis = map_professordisciplina.GetInfo(genome.GetNivelList(nivel)[i]);
                              if(it_profdis->coddisciplina==it_oferecimento->coddisciplina)
                              {
                                   score+=(weight*weight_found_oferecimento);
                                   if(it_profdis->preferencial) score+=(weight*weight_preferencial);
                              }
                         }
                         break;               
               
                    case turnos::MANHA_NOITE:
                         for(int i=turnos::MANHA_BEGIN; i<=turnos::MANHA_END; i++)
                         {
                              if(!genome.GetNivelList(nivel)[i]) continue;
                              it_profdis = map_professordisciplina.GetInfo(genome.GetNivelList(nivel)[i]);
                              if(it_profdis->coddisciplina==it_oferecimento->coddisciplina)
                              {
                                   score+=(weight*weight_found_oferecimento);
                                   if(it_profdis->preferencial) score+=(weight*weight_preferencial);
                              }
                         }

                         for(int i=turnos::NOITE_BEGIN; i<=turnos::NOITE_END; i++)
                         {
                              if(!genome.GetNivelList(nivel)[i]) continue;
                              it_profdis = map_professordisciplina.GetInfo(genome.GetNivelList(nivel)[i]);
                              if(it_profdis->coddisciplina==it_oferecimento->coddisciplina)
                              {
                                   score+=(weight*weight_found_oferecimento);
                                   if(it_profdis->preferencial) score+=(weight*weight_preferencial);
                              }
                         }
                         break;
               }
          } // oferecimentos
     } // niveis

     // Constraint #2 - Manha
     for(int i=turnos::MANHA_BEGIN; i<=turnos::MANHA_END; i+=2)
     {
          int acc_credito1=0;
          int acc_credito2=0;
          for(int nivel=0; nivel<genome.mNiveis; nivel++)
          {
               int profdis_val1 = genome.GetNivelList(nivel)[i];
               int profdis_val2 = genome.GetNivelList(nivel)[i+1];
               
               if((!profdis_val1 && !profdis_val2)) continue;

               if(profdis_val1)
               {
                    it_profdis = map_professordisciplina.GetInfo(profdis_val1);
                    assert(it_profdis!=map_professordisciplina.GetEnd());
                    it_disciplina = map_disciplina.GetInfo(it_profdis->coddisciplina);
                    assert(it_disciplina!=map_disciplina.GetEnd());
                 
                    switch(it_disciplina->creditos)
                    {
                         case 4:
                              acc_credito1+=1;
                              acc_credito2+=1;
                              continue;
                              break;
                    
                         case 2:
                              acc_credito1+=1;
                              break;
                    }
               }

               if(profdis_val2)
               {
                    it_profdis = map_professordisciplina.GetInfo(profdis_val2);
                    assert(it_profdis!=map_professordisciplina.GetEnd());
                    it_disciplina = map_disciplina.GetInfo(it_profdis->coddisciplina);
                    assert(it_disciplina!=map_disciplina.GetEnd());

                    if(it_disciplina->creditos==2) acc_credito2+=1;
               }
          }
          if(acc_credito1==1) score+=(weight*weight_clash);
          if(acc_credito2==1) score+=(weight*weight_clash);
          
     }
     
     // Constraint #2 - Tarde
     for(int i=turnos::TARDE_BEGIN; i<=turnos::TARDE_END; i+=2)
     {
          int acc_credito1=0;
          int acc_credito2=0;
          for(int nivel=0; nivel<genome.mNiveis; nivel++)
          {
               int profdis_val1 = genome.GetNivelList(nivel)[i];
               int profdis_val2 = genome.GetNivelList(nivel)[i+1];
               
               if((!profdis_val1 && !profdis_val2)) continue;

               if(profdis_val1)
               {
                    it_profdis = map_professordisciplina.GetInfo(profdis_val1);
                    assert(it_profdis!=map_professordisciplina.GetEnd());
                    it_disciplina = map_disciplina.GetInfo(it_profdis->coddisciplina);
                    assert(it_disciplina!=map_disciplina.GetEnd());
                 
                    switch(it_disciplina->creditos)
                    {
                         case 4:
                              acc_credito1+=1;
                              acc_credito2+=1;
                              continue;
                              break; // << imposible
                    
                         case 2:
                              acc_credito1+=1;
                              break;
                    }
               }

               if(profdis_val2)
               {
                    it_profdis = map_professordisciplina.GetInfo(profdis_val2);
                    assert(it_profdis!=map_professordisciplina.GetEnd());
                    it_disciplina = map_disciplina.GetInfo(it_profdis->coddisciplina);
                    assert(it_disciplina!=map_disciplina.GetEnd());

                    if(it_disciplina->creditos==2) acc_credito2+=1;
               }
          }
          if(acc_credito1==1) score+=(weight*weight_clash);
          if(acc_credito2==1) score+=(weight*weight_clash);
     }
     
     // Constraint #2 - Noite
     for(int i=turnos::NOITE_BEGIN; i<=turnos::NOITE_END; i+=2)
     {
          int acc_credito1=0;
          int acc_credito2=0;
          for(int nivel=0; nivel<genome.mNiveis; nivel++)
          {
               int profdis_val1 = genome.GetNivelList(nivel)[i];
               int profdis_val2 = genome.GetNivelList(nivel)[i+1];
               
               if((!profdis_val1 && !profdis_val2)) continue;

               if(profdis_val1)
               {
                    it_profdis = map_professordisciplina.GetInfo(profdis_val1);
                    assert(it_profdis!=map_professordisciplina.GetEnd());
                    it_disciplina = map_disciplina.GetInfo(it_profdis->coddisciplina);
                    assert(it_disciplina!=map_disciplina.GetEnd());
                 
                    switch(it_disciplina->creditos)
                    {
                         case 4:
                              acc_credito1+=1;
                              acc_credito2+=1;
                              continue;
                              break; // << impossible
                    
                         case 2:
                              acc_credito1+=1;
                              break;
                    }
               }

               if(profdis_val2)
               {
                    it_profdis = map_professordisciplina.GetInfo(profdis_val2);
                    assert(it_profdis!=map_professordisciplina.GetEnd());
                    it_disciplina = map_disciplina.GetInfo(it_profdis->coddisciplina);
                    assert(it_disciplina!=map_disciplina.GetEnd());

                    if(it_disciplina->creditos==2) acc_credito2+=1;
               }
          }
          if(acc_credito1==1) score+=(weight*weight_clash);
          if(acc_credito2==1) score+=(weight*weight_clash);
     }
     

     // Bonus para dias com 2 disciplinas de 2 creditos
     for(int nivel=0; nivel<genome.mNiveis; nivel++)
     {
          for(int i=turnos::MANHA_BEGIN; i<=turnos::NOITE_END; i+=2)
          {
               int element = genome.GetNivelList(nivel)[i];
               int element2 = genome.GetNivelList(nivel)[i+1];
               
               if(!element || !element2) continue;
               
               it_profdis = map_professordisciplina.GetInfo(element);
               assert(it_profdis!=map_professordisciplina.GetEnd());
               
               it_disciplina = map_disciplina.GetInfo(it_profdis->coddisciplina);
               assert(it_disciplina!=map_disciplina.GetEnd());
               
               if(it_disciplina->creditos!=2) continue;
               
               it_profdis = map_professordisciplina.GetInfo(element2);
               assert(it_profdis!=map_professordisciplina.GetEnd());
               
               if(it_disciplina->creditos!=2) continue;
               
               score+=(weight*weight_stillturn);
          }
     }
     
     // Constraint #x - Laboratorios, bonus para individuo que nao
     // exceder o maximo de laboratorios do curso
     for(int nivel=0; nivel<genome.mNiveis; nivel++)
     {
          for(int i=turnos::MANHA_BEGIN; i<=turnos::NOITE_END; i++)
          {
               int element = genome.GetNivelList(nivel)[i];
               if(!element) continue;
               
               it_profdis = map_professordisciplina.GetInfo(element);
               assert(it_profdis!=map_professordisciplina.GetEnd());
               
               it_disciplina = map_disciplina.GetInfo(it_profdis->coddisciplina);
               assert(it_disciplina!=map_disciplina.GetEnd());
               
               laboratorio_slot[i]+= it_disciplina->laboratorio;
               
          }
     }
     
     it_curso = map_curso.GetInfo(g_options.timetabling_curso);
     assert(it_curso!=map_curso.GetEnd());

     for(int i=0; i<turnos::TAMANHO; i++)
          if(laboratorio_slot[i] <= it_curso->laboratorios)
               score+=(weight*weight_laboratorio);
               

     // Horas do professor e FERIADO
     for(int nivel=0; nivel<genome.mNiveis; nivel++)
     {
          for(int i=turnos::MANHA_BEGIN; i<=turnos::NOITE_END; i++)
          {
               int element = genome.GetNivelList(nivel)[i];
               if(!element) continue;
               
               it_profdis = map_professordisciplina.GetInfo(element);
               assert(it_profdis!=map_professordisciplina.GetEnd());
               
               it_disciplina = map_disciplina.GetInfo(it_profdis->coddisciplina);
               assert(it_disciplina!=map_disciplina.GetEnd());
               
               // Feriado
               if(!it_disciplina->podecairemferiado)
                    score-=(map_diasemana.GetFeriados(i)*weight_feriado);
               
               if(professor_horas.count(it_profdis->codprofessor))
                    professor_horas[it_profdis->codprofessor]+=(it_disciplina->creditos);
               else
                    professor_horas[it_profdis->codprofessor]=it_disciplina->creditos;
          }
     }

     for(std::map<int,int>::iterator it=professor_horas.begin();
         it!=professor_horas.end();
         it++)
     {
          it_professor = map_professor.GetInfo(it->first);
          assert(it_professor!=map_professor.GetEnd());
          
          if(it->second <= it_professor->horas)
               score+=(weight*weight_horas);
     }
     
     // Disponibilidade
     for(int nivel=0; nivel<genome.mNiveis; nivel++)
     {
          for(int i=turnos::MANHA_BEGIN; i<=turnos::NOITE_END; i++)
          {
               int element = genome.GetNivelList(nivel)[i];
               bool must_elect = true;
               if(!element) continue;

               it_profdis = map_professordisciplina.GetInfo(element);
               assert(it_profdis!=map_professordisciplina.GetEnd());

               for(it_disponibilidade=map_disponibilidade.GetBegin();
                   it_disponibilidade!=map_disponibilidade.GetEnd();
                   it_disponibilidade++)
               {    
                    if(it_disponibilidade->codprofessor != it_profdis->codprofessor)
                         continue;
                    if(it_disponibilidade->codtimeslot == i) must_elect = false;
               }
               
               if(must_elect) score+=(weight*weight_disponibilidade);
          }
     }     

     assert(score>=0.0);
     return score;
}

// building blocks (break)
int TimeGenome::CrossOnePointTurno(const GAGenome& mom, const GAGenome &dad,
                      GAGenome *sis, GAGenome *bro)
{
     int cross = 0;
     TimeGenome &dadgen = (TimeGenome &)dad;
     TimeGenome &momgen = (TimeGenome &)mom;

     // 0, 2, 4, 6, 8, 10, 12
     int crosspoint_manha = GARandomInt(0,6) * 2;
     // 12, 14, 16, 18, 20, 22     
     int crosspoint_tarde = GARandomInt(6,11) * 2;
     // 22, 24, 26, 28, 30, 32
     int crosspoint_noite = GARandomInt(11, 16) * 2;

     // Crossover ideas:
     // 1) otimizar o "espaço" no vetor
     // 2) uniform crossover
    
     if(sis)
     {
          TimeGenome &sister = (TimeGenome &)*sis;
          for(int i=0; i<momgen.mNiveis; i++)
          {
               // Manhã (0-11)
               for(int j=turnos::MANHA_BEGIN; j<crosspoint_manha; j++)
                    sister.GetNivelList(i)[j] = momgen.GetNivelList(i)[j];
               for(int j=crosspoint_manha; j<=turnos::MANHA_END; j++)
                    sister.GetNivelList(i)[j] = dadgen.GetNivelList(i)[j];
                    
               // Tarde (12-21)
               for(int j=turnos::TARDE_BEGIN; j<crosspoint_tarde; j++)
                    sister.GetNivelList(i)[j] = momgen.GetNivelList(i)[j];
               for(int j=crosspoint_tarde; j<=turnos::TARDE_END; j++)
                    sister.GetNivelList(i)[j] = dadgen.GetNivelList(i)[j];
                    
               // Noite (22-31)
               for(int j=turnos::NOITE_BEGIN; j<crosspoint_noite; j++)
                    sister.GetNivelList(i)[j] = momgen.GetNivelList(i)[j];
               for(int j=crosspoint_noite; j<=turnos::NOITE_END; j++)
                    sister.GetNivelList(i)[j] = dadgen.GetNivelList(i)[j];
          }
          cross++;
          sister._evaluated = gaFalse;;
     }
     
     if(bro)     
     {
          TimeGenome &brother = (TimeGenome &)*bro;
          for(int i=0; i<dadgen.mNiveis; i++)
          {    
               // Manhã (0-11)
               for(int j=turnos::MANHA_BEGIN; j<crosspoint_manha; j++)
                    brother.GetNivelList(i)[j] = dadgen.GetNivelList(i)[j];
               for(int j=crosspoint_manha; j<=turnos::MANHA_END; j++)
                    brother.GetNivelList(i)[j] = momgen.GetNivelList(i)[j];
                    
               // Tarde (12-21)
               for(int j=turnos::TARDE_BEGIN; j<crosspoint_tarde; j++)
                    brother.GetNivelList(i)[j] = dadgen.GetNivelList(i)[j];
               for(int j=crosspoint_tarde; j<=turnos::TARDE_END; j++)
                    brother.GetNivelList(i)[j] = momgen.GetNivelList(i)[j];
                    
               // Noite (22-31)
               for(int j=turnos::NOITE_BEGIN; j<crosspoint_noite; j++)
                    brother.GetNivelList(i)[j] = dadgen.GetNivelList(i)[j];
               for(int j=crosspoint_noite; j<=turnos::NOITE_END; j++)
                    brother.GetNivelList(i)[j] = momgen.GetNivelList(i)[j];
          }
          cross++;
          brother._evaluated = gaFalse;
     }

     return cross;
}

int TimeGenome::CrossOnePointNivel(const GAGenome& mom, const GAGenome &dad,
                      GAGenome *sis, GAGenome *bro)
{
     int cross = 0;
     TimeGenome &dadgen = (TimeGenome &)dad;
     TimeGenome &momgen = (TimeGenome &)mom;

     if(dadgen.mNiveis!=momgen.mNiveis) return cross;
     if(dadgen.mNiveis<2) return cross;
     
     int rand_point = GARandomInt(1, dadgen.mNiveis-1);

     if(sis)
     {
          TimeGenome &sister = (TimeGenome &)*sis;

          for(int i=0; i<rand_point; i++)
               sister.GetNivelList(i) = momgen.GetNivelList(i);

          for(int i=rand_point; i<momgen.mNiveis; i++)
               sister.GetNivelList(i) = dadgen.GetNivelList(i);

          cross++;
          sister._evaluated = gaFalse;
     }     
     
     if(bro)
     {
          TimeGenome &brother = (TimeGenome &)*bro;
          
          for(int i=0; i<rand_point; i++)
               brother.GetNivelList(i) = dadgen.GetNivelList(i);

          for(int i=rand_point; i<momgen.mNiveis; i++)
               brother.GetNivelList(i) = momgen.GetNivelList(i);

          cross++;
          brother._evaluated = gaFalse;
     }


     return cross;
}

